package com.jsjvm.client.classparser;

import java.util.Vector;

import com.jsjvm.client.classparser.JAttribute.CodeAttribute;
import com.jsjvm.client.classparser.JAttribute.ConstantValueAttribute;
import com.jsjvm.client.util.DataInputStream;

/**
 * Information about members of the class: mehtods, static methods, fields
 * 
 * 
 * @author ctalau
 */
public class JMember {
	public static final int STATIC = 0x0008;
	public static final int NATIVE = 0x0100;
	
	protected int flags; // static or not?
	
	protected String memberName;
	protected String fullName;
	protected String type;
	protected Vector<JAttribute> attributes = new Vector<JAttribute>();
	protected int id = 0;

	public JMember(DataInputStream is, JClass c, int id) {
		JConstantPool cpool = c.cpool;
		this.id = id;

		this.flags = is.readUShort();
		String name = cpool.getString(is.readUShort());
		String type = cpool.getString(is.readUShort());

		this.memberName = name + type;
		this.type = type;
		this.fullName = c.name + "/" + memberName;

		
		int attrCnt = is.readUShort();
		for (int i = 0; i < attrCnt; i++)
			attributes.add(JAttribute.readAttribute(is, cpool));
	}
	
	public JMember(){}

	@Override
	public String toString() {
		return this.memberName + " : " + attributes;
	}

	public boolean isStatic(){
		return (flags & STATIC) == STATIC;
	}
	
	public String getMemberName() {
		return memberName;
	}
	
	public String getFullName() {
		return fullName;
	}
	
	public int getID(){
		return id;
	}
	
	/**
	 * JMethod contains the [code] of the method and the size of it's arguments
	 * on the stack. 
	 * 
	 * @author ctalau
	 */
	public static class JMethod extends JMember {
		protected CodeAttribute code;
		// size of the arguments on the stack, including dispatch object
		@SuppressWarnings("unused")
		private int argsSize, retSize;

		public JMethod(DataInputStream is, JClass c, int id) {
			super(is, c, id);
			for (JAttribute a : attributes)
				if (a.getName().equals("Code"))
					code = (CodeAttribute) a;
			
			this.argsSize = JType.argsSize(type);
			// Instance methods have on the stack the dispatch object
			if (!isStatic()){
				this.argsSize++;
			}
			this.retSize = JType.retSize(type);
		}
		
		private JMethod(){
		}		
		
		private static JMethod bootMehtod = new JMethod(){{
			this.flags = STATIC;
			this.memberName = this.fullName = "<initmethod>";
			this.code = CodeAttribute.getBootCode();	
		}};
		
		public static JMethod getBootMehtod() {
			return bootMehtod;
		}
		
		@Override
		public String toString() {
			return fullName;
		}
		
		public CodeAttribute getCode() {
			return code;
		}
		
		public int getArgsSize() {
			return argsSize;
		}
		
		public boolean isNative(){
			return (flags & NATIVE) == NATIVE;
		}
		
		public static final String CLINIT_METHOD_NAME = "<clinit>()V";
		public static final String STRING_INIT_METHOD_NAME = "<init>([C)V";
	}

	/**
	 * JField contains the initialization value for a field as well as it's size
	 * 
	 * @author ctalau
	 */
	public static class JField extends JMember {
		private Object initValue;
		private int size;

		public JField(DataInputStream is, JClass c, int id) {
			super(is, c, id);
			for (JAttribute a : attributes)
				if (a.getName().equals("ConstantValue"))
					initValue = ((ConstantValueAttribute) a).getValue();
			if (initValue == null)
				initValue = JType.getDefaultValue(type);
			this.size = JType.size(type);
		}
		
		public Object getInitValue() {
			return initValue;
		}
		
		public int getSize() {
			return size;
		}
	}
}
